package fr.pud.client.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import fr.pud.client.controller.ModelObserver;
import fr.pud.client.i18n.LanguagesConstants;
import fr.pud.utils.DOMParser;
import fr.pud.utils.Tools;

/**
 * The representation of a Round as a Collection of route
 * Apr 9, 2012 - OSE.
 * @author Simon Devineau, Adrien Hierry, Leopold Houdin, Pierre Reliquet
 *         Ecole des Mines de Nantes
 *         Routes.java
 */
public class Routes extends Observable implements Serializable, Observer {
    /**
     * The serial UID
     */
    private static final long serialVersionUID = 8251875377844578769L;
    /**
     * The singleton implementation.
     */
    private static Routes     instance         = null;

    /**
     * @return the unique instance of Routes.
     */
    public static Routes getInstance() {
        if (instance == null) {
            instance = new Routes();
        }
        return instance;
    }

    /**
     * Load a routes
     * @return an arrayList of fleet
     */
    @SuppressWarnings("unchecked")
    public static HashSet<Route> loadRoutes(File aFile) {
        HashSet<Route> toReturn = new HashSet<Route>();
        if (aFile.exists()) {
            try {
                FileInputStream fis = new FileInputStream(aFile);
                ObjectInputStream ois = new ObjectInputStream(fis);
                try {
                    toReturn = (HashSet<Route>) ois.readObject();
                }
                finally {
                    try {
                        ois.close();
                    }
                    finally {
                        fis.close();
                    }
                }
            }
            catch (IOException ioe) {
                Tools.writeToLogFile(
                        LanguagesConstants.LANGUAGE
                                .getString(LanguagesConstants.ERROR_FILE)
                                + " : " + aFile, true, ioe.getStackTrace());
            }
            catch (ClassNotFoundException cnfe) {
                Tools.writeToLogFile(
                        LanguagesConstants.LANGUAGE
                                .getString(LanguagesConstants.ERROR_FILE)
                                + " : " + aFile, true, cnfe.getStackTrace());
            }
        }
        return toReturn;
    }

    /**
     * The collection of route
     */
    private Collection<Route> routes;

    /**
     * Default constructor, constructs a new Collection of route.
     */
    public Routes() {
        this.routes = new HashSet<Route>();
        this.addObserver(ModelObserver.getInstance());
    }

    /**
     * @param r
     *            the route to add.
     * @return true if the route was added.
     */
    public boolean add(Route r) {
        if (this.routes.add(r)) {
            r.addObserver(this);
            this.setChanged();
            this.notifyObservers(r);
            return true;
        }
        return false;
    }

    /**
     * @return the collection of route.
     */
    public Collection<Route> getRoutes() {
        return this.routes;
    }

    /**
     * @param r
     *            the route to remove
     * @return true if the route was removed.
     */
    public boolean remove(Route r) {
        if (this.routes.remove(r)) {
            r.deleteObserver(this);
            this.setChanged();
            this.notifyObservers();
            return true;
        }
        return false;
    }

    /**
     * @param r
     *            the station to remove from the routes.
     * @return the number of times that the station had been removed.
     */
    public int removeAll(PUDStation r) {
        int number = 0;
        Iterator<Route> it = this.routes.iterator();
        while (it.hasNext()) {
            if (it.next().remove(r)) {
                number++;
            }
        }
        return number;
    }

    /**
     * @param routes
     *            the new routes to set.
     */
    public void setRoutes(Collection<Route> routes) {
        for (Route route : routes) {
            this.add(route);
        }
    }

    /**
     * @param routes
     *            the new routes to set.
     */
    @SuppressWarnings("static-access")
    public void setRoutes(Routes aRoutes) {
        this.setRoutes(aRoutes.getInstance().getRoutes());
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        String toReturn = "";
        int index = 1;
        for (Route r : this.routes) {
            toReturn += LanguagesConstants.LANGUAGE
                    .getString(LanguagesConstants.ROUTES)
                    + " n°"
                    + index
                    + " :\n" + r;
        }
        return toReturn;
    }

    /**
     * @return a new Document which contains the xml representation of the
     *         routes.
     */
    public Document toXML() {
        Document d = DOMParser.getEmptyDOMTree();
        Element root = this.toXML(d);
        d.appendChild(root);
        return d;
    }

    /**
     * @param d
     *            the document linked.
     * @return the new element which contains a xml representation of the routes
     *         with "routes" as root element.
     */
    public Element toXML(Document d) {
        return this.toXML(d, "routes");
    }

    /**
     * @param d
     *            the document linked.
     * @param name
     *            the name of the root element.
     * @return a new element which contains a xml representation of the routes.
     */
    public Element toXML(Document d, String name) {
        Element root = d.createElement(name);
        Iterator<Route> it = this.getRoutes().iterator();
        while (it.hasNext()) {
            root.appendChild(it.next().toXML(d));
        }
        return root;
    }

    /**
     * Write an xml representation of the routes into an XML file.
     * @param fileName
     */
    public void toXML(String fileName) {
        DOMParser.writeOnDisk(this.toXML(), fileName);
    }

    /**
     * If a route is modified, this method updates the singleton Routes.
     */
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("galere");
        this.setChanged();
        this.notifyObservers(new Integer(((ArrayList<Route>) this.routes)
                .indexOf(o)));
    }
}
